/*
  author Sylvain Bertrand <sylvain.bertrand@gmail.com>
  Protected by linux GNU GPLv2
  Copyright 2012-2014
*/
#include <linux/pci.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/wait.h>

#include <alga/alga.h>
#include <alga/rng_mng.h>
#include <alga/timing.h>
#include <uapi/alga/pixel_fmts.h>
#include <uapi/alga/amd/dce6/dce6.h>

#include "mc.h"
#include "ih.h"
#include "rlc.h"
#include "fence.h"
#include "ring.h"
#include "dmas.h"
#include "ba.h"
#include "cps.h"
#include "gpu.h"
#include "drv.h"

#include "regs.h"

void fence_init_once(struct fence *f)
{
	init_waitqueue_head(&f->wait_queue);	
}

void fence_init(struct fence *f, u32 __iomem *cpu_addr)
{
	atomic_set(&f->bottom, 0);
	atomic_set(&f->top, 0);
	f->cpu_addr = cpu_addr;
}

u32 fence_seq_n_get(struct fence *f)
{
	return (u32)atomic_inc_return(&f->top);
}

#define SIGNALED	1
#define UNSIGNALED	0
/* we presume top will never warp around and catch up bottom and fences */
static u8 is_signaled(struct fence *f, u32 seq_n)
{
	u32 bottom;
	u32 top;

	bottom = (u32)atomic_read(&f->bottom);
	top = (u32)atomic_read(&f->top);

	if (bottom <= top) {
		if (seq_n <= bottom) 
			return SIGNALED;
	} else if (top < bottom) {
		if ((top < seq_n) && (seq_n <= bottom))
			return SIGNALED;
	}
	return UNSIGNALED;
}
#undef SIGNALED
#undef UNSIGNALED

/*
 * This function will wait for the fence to signal for a maximum amount of
 * time defined by timeouts_max_n and timeout_us.
 */
long fence_wait(struct fence *f, u32 seq_n, u32 timeouts_max_n, u32 timeout_us)
{
	int r;

	while (timeouts_max_n--) {
		r = wait_event_timeout(f->wait_queue, is_signaled(f, seq_n),
						usecs_to_jiffies(timeout_us));
		if (r != 0)
			return FENCE_SIGNALED;
	}
	return -FENCE_TIMEOUT;
}
